/*
 * BlackLightning.PacketRegistry
 * Andy Tidball
 * 
 * Project: Black Lightning
 * Copyright: GNU General Public License
 */

using System;
using System.Collections.Generic;
using BlackLightning.PluginLib.Modules;

namespace BlackLightning {
	/// <summary>
	/// Handles the registration and maintenance of which objects want to receive which types of packets.
	/// </summary>
	internal class PacketRegistry {
		// static member data
		private static IPacketHandler _RemoveObject; // the object to match against in MatchesStaticObject

		/// <summary>
		/// Predicate that checks whether the registration's object matches the static _RemoveObject.
		/// </summary>
		/// <param name="Registration">The registration whose object to check.</param>
		/// <returns>True if the registration's object matches _RemoveObject, false otherwise.</returns>
		private static bool MatchesStaticObject(PacketRegistration Registration) {
			return (Registration.Object == _RemoveObject);
		}

		// member data
		private List<PacketRegistration> _Registrations; // a list of registrations

		/// <summary>
		/// Creates a new PacketRegistry.
		/// </summary>
		public PacketRegistry() {
			_Registrations = new List<PacketRegistration>();
		}

		/// <summary>
		/// Registers the given object to receive any packets which match the given filter.
		/// </summary>
		/// <param name="Object">The object registering a filter.</param>
		/// <param name="Filter">The filter to register for the given object.</param>
		public void Register(IPacketHandler Object, BLPacketFilter Filter, Priority Priority, bool NewThread) {
			// add the registration to the list
			_Registrations.Add(new PacketRegistration(Object, Filter, Priority, NewThread));
		}

		/// <summary>
		/// Unregisters the given object from receiving packets which match the given filter.
		/// </summary>
		/// <param name="Object">The object unregistering a filter.</param>
		/// <param name="Filter">The filter to unregister for the given object.</param>
		public void Unregister(IPacketHandler Object, BLPacketFilter Filter) {
			// find a matching registration
			int RemoveIndex = -1;
			for (int i=0; i<_Registrations.Count; ++i) {
				if (_Registrations[i].Object == Object && _Registrations[i].Filter == Filter) {
					RemoveIndex = i;
					break;
				}
			}

			// if there was a matching registration, remove it
			if (RemoveIndex >= 0) {
				_Registrations.RemoveAt(RemoveIndex);
			}
		}

		/// <summary>
		/// Unregisters all the filters that have been registered by the given object.
		/// </summary>
		/// <param name="Object">The object whose filters to unregister.</param>
		public void Unregister(IPacketHandler Object) {
			_RemoveObject = Object;
			_Registrations.RemoveAll(MatchesStaticObject);
			_RemoveObject = null;
		}

		/// <summary>
		/// Queries the registry for any registrations which match the given packet.
		/// </summary>
		/// <param name="Packet">The packet to match against the registry.</param>
		/// <returns>A RegistrationQuery that contains the results of the query.</returns>
		public RegistrationQuery Query(BLPacket Packet) {
			// run through each registration
			List<PacketRegistration> Matches = new List<PacketRegistration>();
			foreach (PacketRegistration Registration in _Registrations) {
				// check if this registration statically matches the given packet
				if (Registration.Filter.StaticMatch(Packet)) {
					// it matches, add it to the list
					Matches.Add(Registration);
				}
			}

			// create a new query with the statically matching list and return it
			return new RegistrationQuery(Matches);
		}
	}

	/// <summary>
	/// Represents a single registration of an object wanting to receive packets which pass a filter.
	/// </summary>
	internal class PacketRegistration : IComparable {
		// member data
		private IPacketHandler _Object;    // the object that made the registration
		private BLPacketFilter _Filter;    // the filter the object has registered
		private Priority       _Priority;  // the priority level that the object wishes to handle matching packets at
		private bool           _NewThread; // whether or not the object wishes to receive matching packets in a new thread

		/// <summary>
		/// Creates a new PacketRegistration.
		/// </summary>
		/// <param name="Object">The object registering the filter.</param>
		/// <param name="Filter">The filter being registered.</param>
		/// <param name="Priority">The priority level that the object wishes to receive matching packets at.</param>
		/// <param name="NewThread">Whether or not the object wishes to receive matching packets in a new thread.</param>
		public PacketRegistration(IPacketHandler Object, BLPacketFilter Filter, Priority Priority, bool NewThread) {
			_Object    = Object;
			_Filter    = Filter;
			_Priority  = Priority;
			_NewThread = NewThread;
		}

		/// <summary>
		/// Gets the object which made this registration.
		/// </summary>
		public IPacketHandler Object {
			get {
				return _Object;
			}
		}
		
		/// <summary>
		/// Gets the filter that is matched against packets.
		/// </summary>
		public BLPacketFilter Filter {
			get {
				return _Filter;
			}
		}

		/// <summary>
		/// Gets the priority level at which the object wishes to handle matching packets.
		/// </summary>
		public Priority Priority {
			get {
				return _Priority;
			}
		}

		/// <summary>
		/// Gets whether or not the object wishes to handle matching packets in a new thread.
		/// </summary>
		public bool NewThread {
			get {
				return _NewThread;
			}
		}

		/// <summary>
		/// Compares two instances of PacketRegistration.
		/// </summary>
		/// <param name="Obj">The instance to compare to this one.</param>
		/// <returns>-1 if this &lt; Obj, 1 if this &gt; Obj, 0 if this == Obj</returns>
		public int CompareTo(object Obj) {
			// make sure we got a PacketRegistration
			PacketRegistration P = Obj as PacketRegistration;
			if (P == null) {
				throw new ArgumentException("Cannot compare a PacketRegistration to a '" + Obj.ToString() + "'.");
			}

			// compare the priorities and return the result
			return ((int)this.Priority).CompareTo((int)P.Priority);
		}
	}

	/// <summary>
	/// Represents a query of the PacketRegistry about which registrations match a given packet.
	/// </summary>
	internal class RegistrationQuery {
		// member data
		private List<PacketRegistration>            _Registrations; // the list of registrations involved in the query
		private List<PacketRegistration>.Enumerator _Enumerator;    // the registration currently being handled

		/// <summary>
		/// Creates a new RegistrationQuery.
		/// </summary>
		/// <param name="Registrations">The list of registrations which are involved in the query.</param>
		public RegistrationQuery(List<PacketRegistration> Registrations) {
			_Registrations = Registrations;
			_Registrations.Sort(); // PacketRegistrations sort by Priority, highest to lowest
			_Enumerator = _Registrations.GetEnumerator();
		}

		/// <summary>
		/// Retrieves the PacketRegistration next in priority order that matches the current state of the given packet.
		/// </summary>
		/// <param name="Packet">The packet to match dynamically against the registrations.</param>
		/// <returns>The next PacketRegistration which matches or null if there are no more.</returns>
		public PacketRegistration GetNext(BLPacket Packet) {
			// keep going until we've returned already or there are no more registrations
			while (_Enumerator.MoveNext()) {
				// check if the current registration matches the current state of the packet
				if (_Enumerator.Current.Filter.DynamicMatch(Packet)) {
					// it matches, return the current registration
					return _Enumerator.Current;
				}
			}

			// no more registrations
			return null;
		}
	}
}
